//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "aura.h"
#include "private.h"
#include <time.h>
#include <errno.h>
#include <linux/futex.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <pthread.h>

typedef struct _EventDescr {
    int count;
    int state;
    Aura_Bool bManual;
    pthread_cond_t *m_pCond;
    pthread_mutex_t *m_pMutex;
} EventDescr;

AURA_API AuraEvent_Create(
        /* [in] */ Aura_Bool bManualReset,
        /* [in] */ Aura_EventState initialState,
        /* [out] */ Aura_Handle  * phEvent)
{
    EventDescr *pDescr = NULL;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != phEvent);

    pDescr = (EventDescr *)Aura_malloc(sizeof(EventDescr));
    if (NULL == pDescr) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }

    pDescr->m_pCond = (pthread_cond_t *)Aura_malloc(sizeof(pthread_cond_t));
    if (NULL == pDescr->m_pCond) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT; 
    }

    pDescr->m_pMutex = (pthread_mutex_t *)Aura_malloc(sizeof(pthread_mutex_t));
    if (NULL == pDescr->m_pMutex) {
        ec = AURA_E_OUT_OF_MEMORY;
        goto E_FAIL_EXIT;
    }

    pthread_cond_init(pDescr->m_pCond, NULL);
    pthread_mutex_init(pDescr->m_pMutex, NULL);

    pDescr->bManual = bManualReset;
    pDescr->state = initialState;
    pDescr->count = (int)(AURA_UNSIGNALED != initialState);

    *phEvent = (Aura_Handle)pDescr;
    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pDescr) {
        if (NULL != pDescr->m_pMutex) {
            pthread_mutex_destroy(pDescr->m_pMutex);
        }
        if (NULL != pDescr->m_pCond) {
            pthread_cond_destroy(pDescr->m_pCond);
        }

        Aura_free(pDescr->m_pMutex);
        Aura_free(pDescr->m_pCond);
        Aura_free(pDescr);
    }
    return ec;
}

AURA_API AuraEvent_Wait(
        /* [in] */ Aura_Handle hEvent,
        /* [in] */ Aura_Millisecond32 timeOut,
        /* [out] */ Aura_WaitResult * pResult,
        /* [out] */ Aura_EventState * pState)
{
    EventDescr *pEvent = (EventDescr *)(hEvent);
    Aura_EventState temp = AURA_UNSIGNALED;
    struct timespec ts, curTs;
    int mseconds = 0;
    int ret = 0;

    assert(NULL != pEvent);
    assert(NULL != pResult);
    assert(NULL != pState);

    pthread_mutex_lock(pEvent->m_pMutex);
    if (pEvent->bManual
        && AURA_UNSIGNALED != pEvent->state) {
        *pResult = Aura_WaitResult_OK;
        *pState = pEvent->state;
        pthread_mutex_unlock(pEvent->m_pMutex);
        return AURA_NOERROR;
    }

    if (--(pEvent->count) >= 0) {
        assert(!pEvent->bManual);
        *pState = pEvent->state;
        pEvent->state = AURA_UNSIGNALED;
        pEvent->count = 0;
        *pResult = Aura_WaitResult_OK;

        pthread_mutex_unlock(pEvent->m_pMutex);
        return AURA_NOERROR;
    }

    mseconds = InfiniteFilter(timeOut);
    ts.tv_sec  = (time_t)mseconds/1000;
    ts.tv_nsec = (time_t)(mseconds % 1000)*1000*1000;

    clock_gettime(CLOCK_REALTIME, &curTs);
    ts.tv_sec += curTs.tv_sec;
    ts.tv_nsec += curTs.tv_nsec;

    ret = pthread_cond_timedwait(pEvent->m_pCond, pEvent->m_pMutex, &ts);
    if (ret != 0) {
        if (ETIMEDOUT == ret) {
            *pResult = Aura_WaitResult_TimedOut;
            goto NORMAL_EXIT;
        }
        else if (EINTR == ret) {
            *pResult = Aura_WaitResult_Interrupted;
            goto NORMAL_EXIT;
        }
        else {
            pthread_mutex_unlock(pEvent->m_pMutex);
            assert(0);
        }
    }

    temp = pEvent->state;
    if (!pEvent->bManual) {
        if (pEvent->count > 0) {
            pEvent = 0;
        }
        pEvent->state =  AURA_UNSIGNALED;
    }

    *pResult = Aura_WaitResult_OK;
    *pState = temp;

    pthread_mutex_unlock(pEvent->m_pMutex);
    return AURA_NOERROR;

NORMAL_EXIT:
    pEvent->count++;
    pthread_mutex_unlock(pEvent->m_pMutex);
    return AURA_NOERROR;
}

AURA_API AuraEvent_Signal(
        /* [in] */ Aura_Handle hEvent,
        /* [in] */ Aura_EventState state)
{
    EventDescr *pEvent = (EventDescr *)(hEvent);
    int ret = 0;

    assert(NULL != pEvent);
    assert(AURA_UNSIGNALED != state);

    pthread_mutex_lock(pEvent->m_pMutex);
    if (++pEvent->count > 0) {
        pEvent->state = state;
        pthread_mutex_unlock(pEvent->m_pMutex);
        return AURA_NOERROR;
    }

    pEvent->state = state;
    if (pEvent->bManual) {
        ret = pthread_cond_broadcast(pEvent->m_pCond);
    }
    else {
        ret = pthread_cond_signal(pEvent->m_pCond);
    }

    assert(0 == ret);

    pthread_mutex_unlock(pEvent->m_pMutex);
    return AURA_NOERROR;
}

AURA_API AuraEvent_Unsignal(
        /* [in] */ Aura_Handle hEvent)
{
    EventDescr *pEvent = (EventDescr *)(hEvent);

    assert(NULL != pEvent);

    pEvent->count = 0;
    pEvent->state = AURA_UNSIGNALED;

    return AURA_NOERROR;
}

AURA_API AuraEvent_Destroy(
        /* [in] */ Aura_Handle hEvent)
{
    EventDescr *pEvent = (EventDescr *)(hEvent);

    assert(NULL != pEvent);

    pthread_cond_broadcast(pEvent->m_pCond);
    pthread_cond_destroy(pEvent->m_pCond);
    pthread_mutex_destroy(pEvent->m_pMutex);

    Aura_free(pEvent->m_pCond);
    Aura_free(pEvent->m_pMutex);
    Aura_free(pEvent);

    return AURA_NOERROR;
}
